#include "GridData.h"
#include "GridDataReader.h"
#include "VolumeOptions.h"

namespace OCPP
{
	namespace Volume
	{
		GridData GridData::shallowCopy()
		{
			GridData gridData;
			gridData.extent = extent;
			gridData.time = time;
			gridData.level = level;
			gridData.varName = varName;
			gridData.levelUinits = levelUinits;
			gridData.levelData = levelData;
			gridData.minMaxValue = minMaxValue;
			return gridData;
		}

		void GridData::read(const VolumeOptions& options)
		{
			if (dataReader == nullptr) return;
			gridDataValue = dataReader->read(*this,options);

			auto rectangle = extent.rectangle;
			auto dx = (rectangle.east - rectangle.west) / (extent.dimX - 1);

			flipXIf(options);
			flipYIf(options);
			clipIf(options);
			reverseZ(options);
			setPositive(options);

			float scale = 1.0f;
			if (options.customScaler.has_value() && !gridDataValue->doneScaleAndOffset)
			{
				gridDataValue->doneScaleAndOffset = true;
				scale = options.customScaler.value();
			}

			//
			auto& _vecFloat = gridDataValue->_vecFloat;
			auto& _vecDouble = gridDataValue->_vecDouble;
			if (_vecFloat.empty() && _vecDouble.size() > 0)
			{
				_vecFloat.resize(_vecDouble.size());
				for (auto i = 0; i < _vecDouble.size(); i++)
				{
					_vecFloat[i] = (float)_vecDouble[i] * scale;
				}
			}
			else
			{
				for (auto i = 0; i < _vecFloat.size(); i++)
				{
					_vecFloat[i] *=  scale;
				}
			}
		}

		void GridData::flipXIf(const VolumeOptions& options)
		{
			auto& rectangle = extent.rectangle;
			auto dx = (rectangle.east - rectangle.west) / (extent.dimX - 1);
			if (rectangle.west == 0.0 && 
				rectangle.east + dx >= Cesium::CesiumMath::TWO_PI)
			{
				int dimZ = extent.dimZ;
				int dimY = extent.dimY;
				int dimX = extent.dimX;

				auto& _vecFloat = gridDataValue->_vecFloat;
				if (_vecFloat.empty())
				{
					_vecFloat.resize(dimZ * dimY * dimX);
				}

				for (int z = 0; z < dimZ; z++) {
					int idZ = z * dimY * dimX;
					for (int y = 0; y < dimY; y++) {
						int idY = y * dimX;
						for (int x = 0; x < dimX * 0.5; x++) {
							int i0 = idZ + idY + x;
							int i1 = idZ + idY + x + (int)(0.5 * dimX);
							assert(x + 0.5 * dimX != dimX);
							float value0 = gridDataValue->getFloat(i0);
							float value1 = gridDataValue->getFloat(i1);
							_vecFloat[i0] = value1;
							_vecFloat[i1] = value0;
						}
					}
				}

				rectangle.west = -Cesium::CesiumMath::PI;
				rectangle.east = Cesium::CesiumMath::PI;
				gridDataValue->_vecDouble.clear();
			}
		}

		void GridData::flipYIf(const VolumeOptions& options)
		{

		}

		void GridData::clipIf(const VolumeOptions& options)
		{
			if (!options.customClipExtent.has_value())return;
			auto clipExtent = options.customClipExtent.value();

			int dimZ = extent.dimZ;
			int dimY = extent.dimY;
			int dimX = extent.dimX;
			auto gridExtent = extent.rectangle;
			double dx = gridExtent.getWidth() / (dimX - 1);
			double dy = gridExtent.getHeight() / (dimY - 1);
			auto intersectExtent = Cesium::Rectangle::intersection(gridExtent, clipExtent);

			if (options.knownNotLonLat)
			{
				auto west = (std::max)(gridExtent.west, clipExtent.west);
				auto east = (std::min)(gridExtent.east, clipExtent.east);
				auto south = (std::max)(gridExtent.south, clipExtent.south);
				auto north = (std::min)(gridExtent.north, clipExtent.north);
				intersectExtent.west = west;
				intersectExtent.south = south;
				intersectExtent.east = east;
				intersectExtent.north = north;
			}


			int clipStartCol = (int)floor((intersectExtent.west - gridExtent.west) / dx);
			int clipEndCol = (int)ceil((intersectExtent.east - gridExtent.west) / dx) + 1;
			int clipStartRow = (int)floor((intersectExtent.south - gridExtent.south) / dy);
			int clipEndRow = (int)ceil((intersectExtent.north - gridExtent.south) / dy) + 1;
			if (clipEndCol >= dimX) clipEndCol = dimX;
			if (clipEndRow >= dimY) clipEndRow = dimY;
			intersectExtent.west = gridExtent.west + clipStartCol * dx;
			intersectExtent.east = gridExtent.west + (clipEndCol - 1) * dx;
			intersectExtent.south = gridExtent.south + clipStartRow * dy;
			intersectExtent.north = gridExtent.south + (clipEndRow - 1) * dy;
			int clippedDimX = clipEndCol - clipStartCol;
			int clippedDimY = clipEndRow - clipStartRow;

			FloatVector vecData;
			vecData.resize(dimZ * clippedDimY * clippedDimX);
			for (int z = 0; z < dimZ; z++)
			{
				int zOffset0 = z * dimY * dimX;
				int zOffset1 = z * clippedDimY * clippedDimX;
				for (int r = 0; r < clippedDimY; r++)
				{
					int yOffset0 = (r + clipStartRow) * dimX;
					int yOffset1 = r * clippedDimX;

					for (int c = 0; c < clippedDimX; c++)
					{
						int i0 = zOffset0 + yOffset0 + c + clipStartCol;
						int i1 = zOffset1 + yOffset1 + c;

						float value = gridDataValue->getFloat(i0);
						vecData[i1] = value;
					}
				}
			}

			gridDataValue->_vecFloat = vecData;
			extent.dimX = clippedDimX;
			extent.dimY = clippedDimY;
			extent.rectangle = intersectExtent;
		}

		void GridData::setPositive(const VolumeOptions& options)
		{
			if (!options.knownIsPositive
				)return;

			auto& vecData = gridDataValue->_vecFloat;
			for (auto i = 0; i < vecData.size(); i++)
			{
				vecData[i] = abs(vecData[i]);
			}
		}

		void GridData::reverseZ(const VolumeOptions& options)
		{
			if (!options.customReverseZ 
				|| gridDataValue->doneReverseZ
				|| extent.dimZ == 1
				)return;
			gridDataValue->doneReverseZ = true;

			std::sort(levelData.begin(), levelData.end(), [](const auto& a,const auto&b) {
				return a - b < 0;
				});

			int dimZ = extent.dimZ;
			int dimY = extent.dimY;
			int dimX = extent.dimX;

			auto& _vecFloat = gridDataValue->_vecFloat;
			if (_vecFloat.empty())
			{
				_vecFloat.resize(dimZ * dimY * dimX);
			}

			for (int z = 0; z < dimZ * 0.5; z++) {
				int idZ0 = z * dimY * dimX;
				int idZ1 = (dimZ - z - 1) * dimY * dimX;
				for (int y = 0; y < dimY; y++) {
					int idY = y * dimX;
					for (int x = 0; x < dimX; x++) {
						int i0 = idZ0 + idY + x;
						int i1 = idZ1 + idY + x;
						float value0 = gridDataValue->getFloat(i0);
						float value1 = gridDataValue->getFloat(i1);
						_vecFloat[i0] = value1;
						_vecFloat[i1] = value0;
					}
				}
			}
		}

	}
}

